#
import argparse
import sys
from typing import Dict, Set, List, Tuple
import random
import numpy as np
import matplotlib.pyplot as plt
import torch
from torch import nn
import torch.nn.functional as F
from torch.optim.lr_scheduler import ReduceLROnPlateau
from torch.utils.data import Dataset, DataLoader
from torch.utils.tensorboard import SummaryWriter
from apps.tlp.tlp_config import TlpConfig as TG
from apps.tlp.tlp_ds import TlpDs
from apps.tlp.tlp_mlp import TlpMlp

class TlpApp(object):
    IDX_id = 0 # 为预警记录的唯一编号，注意 1208, 1589, 4878, 13017, 21759, 32426, 33790 由于缺少预警开始或结束时间，对应行的数据已删除；
    IDX_did = 1 # 设备识别码；
    IDX_dname = 2 # 设备名称；
    IDX_device_lng = 3 # 设备所在经度；
    IDX_device_lat = 4 # 设备所在纬度；
    IDX_max_elevel = 5 # 预警最大等级（1-5）;
    IDX_warning_start_time = 6 # 预警开始时间；
    IDX_warning_end_time = 7 # 预警结束时间；
    IDX_first_lightning_time = 8 # 预警持续时间内首次闪电发生时间；
    IDX_first_lightning_lng = 9 # 首次闪电发生经度；
    IDX_first_lightning_lat = 10 # 首次闪电发生纬度；
    IDX_first_lightning_distance = 11 # 首次闪电距设备的距离（km）；
    IDX_first_lightning_strength = 12 # 首次闪电发生强度 （kA）；
    IDX_first_lw_level = 13 # 首次闪电发生时对应的设备预警等级；
    IDX_last_lightning_time = 14 # 预警持续时间内末次闪电发生时间；
    IDX_last_lightning_lng = 15 # 末次闪电发生经度；
    IDX_last_lightning_lat = 16 # 末次闪电发生纬度；
    IDX_last_lightning_distance = 17 # 末次闪电距设备的距离（km）；
    IDX_last_lightning_strength = 18 # 末次闪电发生强度 （kA）；
    IDX_last_lw_level = 19 # 末次闪电发生时对应的设备预警等级。   

    def __init__(self):
        self.name = 'apps.tlp.tlp_app.TlpApp'     

    @staticmethod
    def startup(params:Dict = {}) -> None:
        print(f'雷电预测MLP v0.0.1')
        # 设置中文字体
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
        plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
        # TlpApp.dispRawData()
        # TlpApp.dispH23Data()
        # TlpApp.data_distribution()
        # TlpApp.data_analysis(params=params)
        # TlpDs.startup(params=params)
        # TlpApp.train_main(params=params)
        # TlpApp.predict(params=params)
        # TlpApp.visualize(params=params)
        TlpApp.evaluate_main(params=params)

    @staticmethod
    def evaluate_main(params:Dict = {}) -> None:
        '''
        性能评价主程序
        '''
        # 从结果csv文件中，读出每条记录闪电开始下标值和结束下标值
        rec_dict = TlpDs.load_result_csv(csv_fn='work/tlp/datas/warnings1-8.csv')
        TlpApp.evaluate_model(rec_dict=rec_dict, eletric_txt='work/tlp/datas/electric.txt')

    @staticmethod
    def evaluate_model(rec_dict:Dict, eletric_txt:str) -> None:
        # 读入electric.txt文件的每一行
        # 从头开始依次读出5分钟300个数据点作为一帧，步长为60个数据点取下一帧，对该帧判断类型，如果为正常帧或雷电中
        # 不做任何处理，以黄色预警为例（其余预警处理逻辑相同仅时间间隔不同）：
        # 1.1. 若为本行第1个：
        #    1. 雷电开始下标tl_start_idx在[curr_end_idx, curr_end_idx+30*60]内，则为TP+1；
        #    2. 若不在则为FP+1；
        # 1.2. 若不为本行第1个：
        #    1. 已有TP：忽略；
        #    2. 已有FP：
        #        1. 雷电开始下标tl_start_idx在[curr_end_idx, curr_end_idx+30*60]内，则为TP+1；
        #        2. 若不在则忽略；
        num = 1
        model = TlpMlp().to(TG.device)
        with open(eletric_txt, 'r', encoding='utf-8') as rfd:
            for row in rfd:
                arrs0 = row.strip().split(' ')
                rec_id = arrs0[0]
                arrs1 = arrs0[1].split(',')
                print(f'arrs1: {type(arrs1)}; {len(arrs1)}; {arrs1[0]};')
                data = torch.tensor([[float(xi) for xi in arrs1]])
                X = data[:, :300]
                print(f'### X: {X.shape}; ????????????????????????/')
                X = X.to(TG.device)
                y = model(X)
                print(f'X: {X.shape}; y: {y.shape}; {y}; {torch.argmax(y, dim=1)}')
                # rec = torch([float(xi) for xi in arrs1])
                # print(f'rec: {rec.shape};')
                num += 1
                if num > 1:
                    break



    @staticmethod
    def visualize(params:Dict = {}) -> None:
        # 设置中文字体
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
        plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
        # 设置全局字体大小
        plt.rcParams.update({'font.size': 3})
        batch_size = 64
        ds = TlpDs()
        dl = DataLoader(ds, batch_size=batch_size)
        X0s, X1s, X2s, X3s, X4s = [], [], [], [], []
        num = 0
        for bidx, (X, y) in enumerate(dl):
            for idx in range(X.shape[0]):
                if y[idx][0] == 0:
                    X0s.append(X)
                if y[idx][0] == 1:
                    X1s.append(X)
                if y[idx][0] == 2:
                    X2s.append(X)
                if y[idx][0] == 3:
                    X3s.append(X)
                if y[idx][0] == 4:
                    X4s.append(X)
                num += 1
                if num % 100 == 0:
                    print(f'处理完成{num}个样本')
        print(f'样本情况：正常情况：{len(X0s)}; 黄色警报: {len(X1s)}; 橙色警报: {len(X2s)}; 红色警报: {len(X3s)}; 雷电中: {len(X4s)};')
        # 可视化
        fig, ax = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(7, 2), dpi=300)
        plt.subplots_adjust(wspace=0.1, hspace=1.0)
        TlpApp.plot_samples(ax, X0s, '正常情况', 0)
        TlpApp.plot_samples(ax, X1s, '黄色预警', 1)
        TlpApp.plot_samples(ax, X2s, '橙色预警', 2)
        TlpApp.plot_samples(ax, X3s, '红色预警', 3)
        TlpApp.plot_samples(ax, X4s, '雷电预警', 4)
        plt.show()
    @staticmethod
    def plot_samples(ax, Xs, title, row_idx):
        base_offset = 51.0
        axis_x = np.arange(Xs[0].shape[1])
        X = None
        for idx in range(5):
            rv1 = random.randint(0, len(Xs)-1)
            rv2 = random.randint(0, Xs[rv1].shape[0]-1)
            print(f'### rv2: {rv2};')
            X_np = Xs[rv1][rv2].detach().cpu().numpy()
            print(f'### rv2: {rv2}; X_np: {X_np.shape};')
            ax[row_idx][idx].plot(axis_x, 10*np.log10(np.abs(X_np)))
            ax[row_idx][idx].set_title(f'{title}_{idx+1}')
            # ax[row_idx][idx].set_xlabel('变化值')
            # ax[row_idx][idx].set_ylabel('数量')
            ax[row_idx][idx].grid(True)
            # ax[row_idx][idx].set_ylim(-5, 5)

    @staticmethod
    def train_main(params:Dict = {}) -> None:
        pt_fn = './work/tlp/ckpts/mlp.pk'
        # Create a SummaryWriter instance
        TG.writer = SummaryWriter(log_dir='./work/logs/tbs')
        # Download training data from open datasets.
        training_data = TlpDs()
        # Download test data from open datasets.
        test_data = TlpDs()
        batch_size = 64
        # Create data loaders.
        train_dataloader = DataLoader(training_data, batch_size=batch_size)
        test_dataloader = DataLoader(test_data, batch_size=batch_size)
        print(f"Using {TG.device} device")
        model = TlpMlp().to(TG.device)
        # print(model)
        loss_fn = nn.CrossEntropyLoss()        
        # 预热阶段
        print(f'##### 开始预热训练')
        warmup_opt = torch.optim.AdamW(model.parameters(), lr=1e-5, betas=(0.9, 0.99), weight_decay=1e-2)
        warmup_scheduler = ReduceLROnPlateau(warmup_opt, 'min', threshold=1e-4, eps=1e-8)
        warmup_epochs = 5
        for epoch in range(warmup_epochs):
            print(f'    warmup {epoch}......')
            TlpApp.train_epoch(epoch, train_dataloader, model, loss_fn, warmup_opt)
        # 正式训练过程
        print(f'##### 开始正式训练')
        optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, betas=(0.9, 0.99), weight_decay=1e-2)
        scheduler = ReduceLROnPlateau(optimizer, 'min', threshold=1e-4, eps=1e-8)
        epochs = 2000000
        best_loss = sys.float_info.max
        improve_threshold = 1e-7
        cumulative_steps = 0
        max_unimproves = 500
        for epoch in range(epochs):
            print(f"Epoch {epoch+1}\n-------------------------------")
            loss = TlpApp.train_epoch(epoch, train_dataloader, model, loss_fn, optimizer)
            TlpApp.test(epoch, test_dataloader, model, loss_fn)
            cumulative_steps += 1
            if best_loss > loss:
                if best_loss - loss > improve_threshold:
                    print(f'Save best weights')
                    # model.save(pt_fn=pt_fn)
                    torch.save(model.state_dict(), pt_fn)
                    cumulative_steps = 0
                    best_loss = loss
            if cumulative_steps > max_unimproves:
                print(f'Earlly Stopping!!!!!!')
                break
        TG.writer.flush()
        TG.writer.close()

    @staticmethod
    def train_epoch(epoch, dataloader, model, loss_fn, optimizer):
        size = len(dataloader.dataset)
        model.train()
        for batch, (X, y) in enumerate(dataloader):
            X, y = X.to(TG.device), y.to(TG.device).squeeze(1)
            # Compute prediction error
            pred = model(X)
            loss = loss_fn(pred, y)
            # Backpropagation
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()
            if batch % 100 == 0:
                loss, current = loss.item(), (batch + 1) * len(X)
                TG.writer.add_scalar("Loss/train", loss, epoch)
                print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
        return loss

    @staticmethod
    def test(epoch, dataloader, model, loss_fn):
        size = len(dataloader.dataset)
        num_batches = len(dataloader)
        model.eval()
        test_loss, correct = 0, 0
        with torch.no_grad():
            for X, y in dataloader:
                X, y = X.to(TG.device), y.to(TG.device).squeeze(1)
                pred = model(X)
                test_loss += loss_fn(pred, y).item()
                correct += (pred.argmax(1) == y).type(torch.float).sum().item()
        test_loss /= num_batches
        correct /= size
        print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")

    @staticmethod
    def predict(params:Dict = {}) -> None:
        '''
        对electric.txt文件中每一行，读出5分钟的一帧，判断5种类型中的哪种，格式为：
        {
            "id": 1,
            [
                {"cls_id": 1, "idx": 60},
                {"cls_id": 1, "idx": 120},
                ...
                {"cls_id": 4, "idx": 1800}
            ]
        }
        以每一条为单位：
        1. 无雷电有报警：则该类别FP_CLS[cls_id]加1，总体FP加1；
        2. 无雷电无报警：所有类别TN_CLS[cls_id]加1，总体TN加1；
        3. 有雷电未报警：所有类别FN_CLS[cls_id]加1，总体FN加1；
        4. 有雷电有报警：
          4.1. 黄色预警：该条idx后1200~1800个点内有第一次雷电发生时间，TP_CLS[1]加1，总体TP加1；否则FP_CLS[1]加1，总体FP加1；
          4.2. 橙色预警：该条idx后600~1200个点内有第一次雷电发生时间，TP_CLS[2]加1，总体TP加1；否则FP_CLS[2]加1，总体FP加1；
          4.3. 红色预警：该条idx后0~600个点内有第一次雷电发生时间，TP_CLS[3]加1，总体TP加1；否则FP_CLS[3]加1，总体FP加1；
        '''
        pt_fn = './work/tlp/ckpts/mlp.pk'
        model = TlpMlp().to(TG.device)
        model.load_state_dict(torch.load(pt_fn, weights_only=True))
        X = torch.randn(8, 300)
        X = X.to(TG.device)
        y_hat = model(X)
        print(f'X: {X.shape}; y_hat: {y_hat.shape}; {torch.argmax(y_hat, dim=1)};')
        txt_fn = 'work/tlp/datas/electric.txt'
        with open(txt_fn, 'r', encoding='utf-8') as rfd:
            for row in rfd:
                arrs0 = row.strip().split(' ')







        

    @staticmethod
    def load_tl_datas(params: Dict = {}) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
        '''
        从warnings1-8.csv中读出雷电记录和非雷电记录
        返回值：
            normal_rows: 正常情况，即预报发生雷电而实际未发生雷电
            normal_ids：记录编号（与electric.txt）中记录相对应
            tl_rows：雷电记录，即预报发生雷电而实际发生雷电
            tl_ids：记录编号（与electric.txt）中记录相对应
        '''
        normal_rows = []
        normal_ids = set()
        tl_rows = []
        tl_ids = set()
        # 读取所有记录
        csv_fn = 'work/tlp/datas/warnings1-8.csv'
        first_row = True
        with open(csv_fn, 'r', encoding='utf-8') as cfd:
            for row in cfd:
                if first_row:
                    first_row = False
                    continue
                arrs = row.strip().split(',')
                item = {
                    'id': arrs[TlpApp.IDX_id], # 为预警记录的唯一编号，注意 1208, 1589, 4878, 13017, 21759, 32426, 33790 由于缺少预警开始或结束时间，对应行的数据已删除；
                    'did': arrs[TlpApp.IDX_did], # 设备识别码；
                    'dname': arrs[TlpApp.IDX_dname], # 设备名称；
                    'device_lng': arrs[TlpApp.IDX_device_lng], # 设备所在经度；
                    'device_lat': arrs[TlpApp.IDX_device_lat], # 设备所在纬度；
                    'max_elevel': arrs[TlpApp.IDX_max_elevel], # 预警最大等级（1-5）;
                    'warning_start_time': arrs[TlpApp.IDX_warning_start_time], # 预警开始时间；
                    'warning_end_time': arrs[TlpApp.IDX_warning_end_time], # 预警结束时间；
                    'first_lightning_time': arrs[TlpApp.IDX_first_lightning_time], # 预警持续时间内首次闪电发生时间；
                    'first_lightning_lng': arrs[TlpApp.IDX_first_lightning_lng], # 首次闪电发生经度；
                    'first_lightning_lat': arrs[TlpApp.IDX_first_lightning_lat], # 首次闪电发生纬度；
                    'first_lightning_distance': arrs[TlpApp.IDX_first_lightning_distance], # 首次闪电距设备的距离（km）；
                    'first_lightning_strength': arrs[TlpApp.IDX_first_lightning_strength], # 首次闪电发生强度 （kA）；
                    'first_lw_level': arrs[TlpApp.IDX_first_lw_level], # 首次闪电发生时对应的设备预警等级；
                    'last_lightning_time': arrs[TlpApp.IDX_last_lightning_time], # 预警持续时间内末次闪电发生时间；
                    'last_lightning_lng': arrs[TlpApp.IDX_last_lightning_lng], # 末次闪电发生经度；
                    'last_lightning_lat': arrs[TlpApp.IDX_last_lightning_lat], # 末次闪电发生纬度；
                    'last_lightning_distance': arrs[TlpApp.IDX_last_lightning_distance], # 末次闪电距设备的距离（km）；
                    'last_lightning_strength': arrs[TlpApp.IDX_last_lightning_strength], # 末次闪电发生强度 （kA）；
                    'last_lw_level': arrs[TlpApp.IDX_last_lw_level], # 末次闪电发生时对应的设备预警等级。
                }
                if item['first_lightning_time'] == '':
                    normal_rows.append(item)
                    normal_ids.add(item['id'])
                else:
                    tl_rows.append(item)
                    tl_ids.add(item['id'])
        return normal_rows, normal_ids, tl_rows, tl_ids

    @staticmethod
    def load_datas(params:Dict = {}) -> Dict:
        data_fn = 'work/tlp/datas/electric.txt'
        datas = {} # id: np.ndarray
        datas_raw = []
        num = 0
        with open(data_fn, 'r', encoding='utf-8') as rfd:
            for row in rfd:
                arrs0 = row.strip().split(' ')
                if len(arrs0) <= 1:
                    continue
                arrs1 = arrs0[1].split(',')
                item = []
                for x in arrs1:
                    ki = float(x)
                    if ki > 110:
                        print(f'########### {ki};')
                    item.append(ki)
                datas[arrs0[0]] = np.array(item)
                num += 1
                if num % 1000 == 0:
                    print(f'处理{num}行...')
        return datas
    
    @staticmethod
    def cal_normal_pdf(datas:Dict, normal_rows:List, normal_ids:Set) -> Tuple[Dict, np.ndarray, np.ndarray]:
        normal_dict = {} # value: num
        n1 = 0
        for nid in normal_ids:
            if nid not in datas:
                continue
            for nidx in range(datas[nid].shape[0]): # nidx为电场强度变化值
                if datas[nid][nidx] in normal_dict:
                    normal_dict[datas[nid][nidx]] += 1 # 电场强度变化值数量加1
                else:
                    normal_dict[datas[nid][nidx]] = 1
            n1 += 1
            if n1 % 1000 == 0:
                print(f'处理{n1}条正常记录')
        x1_raw = []
        for k, v in normal_dict.items():
            x1_raw.append(k)
        x1_raw.sort() # 将所有电场强度变化值从小到大排序
        y1_raw = [] # 电场强度变化值发生数量
        for x1i in x1_raw:
            y1_raw.append(normal_dict[x1i])
        x1 = np.array(x1_raw)
        y1 = np.array(y1_raw, dtype=np.float32)
        return normal_dict, x1, y1

    @staticmethod
    def cal_tl_pdf(datas:Dict, tl_rows:List, tl_ids:Set) -> Tuple[Dict, np.ndarray, np.ndarray]:
        tl_dict = {} # value: num
        n2 = 0
        for tid in tl_ids:
            if tid not in datas:
                continue
            datas_tl = datas[tid][0:-1]
            for tidx in range(datas_tl.shape[0]):
                if datas_tl[tidx] in tl_dict:
                    tl_dict[datas_tl[tidx]] += 1
                else:
                    tl_dict[datas_tl[tidx]] = 1
            n2 += 1
            if n2 % 1000 == 0:
                print(f'处理{n2}条雷电数据')
        x2_raw = []
        for k, v in tl_dict.items():
            x2_raw.append(k)
        x2_raw.sort()
        y2_raw = []
        for x2i in x2_raw:
            y2_raw.append(tl_dict[x2i])
        x2 = np.array(x2_raw)
        y2 = np.array(y2_raw, dtype=np.float32)
        return tl_dict, x2, y2

    @staticmethod
    def draw_data_pdf(x: np.ndarray, y: np.ndarray, title:str) -> None:
        plt.figure(figsize=(10, 6))
        plt.plot(x, y)
        plt.title(title)
        plt.xlabel('变化值')
        # plt.xlim(-0.1, 0.1)
        # plt.ylim(0, 0.2E6)
        plt.ylabel('数量')
        plt.grid(True)
        plt.legend()
        plt.show()

    @staticmethod
    def draw_data_pdfs(x1: np.ndarray, y1: np.ndarray, x2: np.ndarray, y2: np.ndarray) -> None:
        plt.figure(figsize=(10, 6))
        plt.plot(x1, y1, label='正常情况')
        plt.plot(x2, y2, label='异常情况')
        plt.title('电场强度变化分布')
        plt.xlabel('变化值')
        # plt.xlim(-0.1, 0.1)
        plt.ylim(0, 0.2E6)
        plt.ylabel('数量')
        plt.grid(True)
        plt.legend()
        plt.show()

    @staticmethod
    def draw_data_pdfs_2(x1: np.ndarray, y1: np.ndarray, x2: np.ndarray, y2: np.ndarray) -> None:
        fig, ax = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(7, 2), dpi=300)
        plt.subplots_adjust(wspace=0.1, hspace=0.05)
        ax[0].plot(x1, y1, '', color='r')
        ax[0].set_title('正常情况')
        ax[0].set_xlabel('变化值')
        ax[0].set_ylabel('数量')
        ax[0].grid(True)
        ax[0].legend()
        ax[1].plot(x2, y2, '', color='g')
        ax[1].set_title('雷电情况')
        ax[1].set_xlabel('变化值')
        ax[1].set_ylabel('数量')
        ax[1].grid(True)
        ax[1].legend()
        plt.show()

    @staticmethod
    def data_analysis(params:Dict = {}) -> None:
        print(f'数据分析')
        TP_num, FP_num = 0, 0
        lead_time = 10 # 提前量10分钟，即在首次闪电前10分钟预测
        normal_rows, normal_ids, tl_rows, tl_ids = TlpApp.load_tl_datas(params=params)
        print(f'统计结果: 雷电记录: {len(tl_rows)}; 正常记录: {len(normal_rows)};')
        # 读入所有数据
        datas = TlpApp.load_datas(params=params)
        # 对于雷电记录，找出雷电发生前10分钟和雷电结束10分钟这段时间的数据
        # 统计正常情况下大气电场强度变化的数据分布（柱状图）
        normal_dict, x1, y1 = TlpApp.cal_normal_pdf(datas=datas, normal_rows=normal_rows, normal_ids=normal_ids)
        # 雷电值处理
        tl_dict, x2, y2 = TlpApp.cal_tl_pdf(datas=datas, tl_rows=tl_rows, tl_ids=tl_ids)
        n = x2.shape[0]
        print(f'### {x2[n-1]}, {x2[n-2]}, {x2[n-3]};')
        # 绘制分布曲线
        y1 /= np.max(y1)
        # TlpApp.draw_data_pdf(x1, y1, '正常情况')
        y2 /= np.max(y2)
        # TlpApp.draw_data_pdf(x2, y2, '雷电情况')
        TlpApp.draw_data_pdfs_2(x1, y1, x2, y2)
        

    @staticmethod
    def data_distribution(params:Dict = {}) -> None:
        '''
        绘制数据值柱状图
        '''
        fn = 'F:/yantao/kdb/工作/汤俊课题组/气象雷达/docs/warnings/electric.txt'
        val_set = set()
        val_dict = {}
        num = 0
        with open(fn, 'r', encoding='utf-8') as rfd:
            for row in rfd:
                arrs0 = row.strip().split(' ')
                if len(arrs0) <= 1:
                    continue
                arrs1 = arrs0[1].split(',')
                for x in arrs1:
                    ki = float(x)
                    if ki in val_dict:
                        val_dict[ki] += 1
                    else:
                        val_dict[ki] = 1
                num += 1
                if num % 1000 == 0:
                    print(f'处理{num}行...')
        val_dict[0.0] = 0
        vs = []
        ks = []
        for k, v in val_dict.items():
            if v > 0.075E6:
                vs.append(k)
            ks.append(k)
        ks.sort()
        vs.sort()
        X = np.array(ks)
        y_raw = []
        for ki in ks:
            y_raw.append(val_dict[ki])
        y = np.array(y_raw)
        y_max = np.max(y)
        y_min = np.min(y)
        print(f'共有{len(ks)}个值; {y_min} -> {y_max};\n{vs};')
        # 绘制曲线
        # 设置中文字体
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
        plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
        plt.figure(figsize=(10, 6))
        plt.plot(X, y, label='电场强度变化分布')
        plt.title('电场强度变化分布')
        plt.xlabel('变化值')
        # plt.xlim(-0.1, 0.1)
        plt.ylim(0, 0.2E6)
        plt.ylabel('数量')
        plt.grid(True)
        plt.legend()
        plt.show()

    @staticmethod
    def dispRawData() -> None:
        data_fn = 'F:/yantao/kdb/工作/汤俊课题组/气象雷达/supports/output_23h/electric.txt'
        num = 0
        with open(data_fn, 'r', encoding='utf-8') as rfd:
            for row in rfd:
                row = row.strip()
                arrs0 = row.split(' ')
                rec_id = int(arrs0[0])
                arrs1 = arrs0[1].split(',')
                if rec_id >=2 and rec_id <= 54:
                    rec = np.array([float(item) for item in arrs1], dtype=np.float32)
                    print(f'{num}: {len(arrs1)}; {rec.shape};')
                    x = range(rec.shape[0])
                    plt.plot(x, rec)
                    plt.title(f'rec_{rec_id}')
                    plt.ylim(-20.0, 60.0)
                    plt.show()
                num += 1
                if num>100:
                    break

    

    @staticmethod
    def dispH23Data() -> None:
        csv_fn = 'F:/yantao/kdb/工作/汤俊课题组/气象雷达/docs/warnings/warnings1-8.csv'
        TP_num = 0
        FP_num = 0
        with open(csv_fn, 'r', encoding='utf-8') as rfd:
            first_row = True
            for row in rfd:
                if first_row: # 忽略第一行
                    first_row = False
                    continue
                arrs = row.strip().split(',')
                item = {
                    'id': arrs[TlpApp.IDX_id], # 为预警记录的唯一编号，注意 1208, 1589, 4878, 13017, 21759, 32426, 33790 由于缺少预警开始或结束时间，对应行的数据已删除；
                    'did': arrs[TlpApp.IDX_did], # 设备识别码；
                    'dname': arrs[TlpApp.IDX_dname], # 设备名称；
                    'device_lng': arrs[TlpApp.IDX_device_lng], # 设备所在经度；
                    'device_lat': arrs[TlpApp.IDX_device_lat], # 设备所在纬度；
                    'max_elevel': arrs[TlpApp.IDX_max_elevel], # 预警最大等级（1-5）;
                    'warning_start_time': arrs[TlpApp.IDX_warning_start_time], # 预警开始时间；
                    'warning_end_time': arrs[TlpApp.IDX_warning_end_time], # 预警结束时间；
                    'first_lightning_time': arrs[TlpApp.IDX_first_lightning_time], # 预警持续时间内首次闪电发生时间；
                    'first_lightning_lng': arrs[TlpApp.IDX_first_lightning_lng], # 首次闪电发生经度；
                    'first_lightning_lat': arrs[TlpApp.IDX_first_lightning_lat], # 首次闪电发生纬度；
                    'first_lightning_distance': arrs[TlpApp.IDX_first_lightning_distance], # 首次闪电距设备的距离（km）；
                    'first_lightning_strength': arrs[TlpApp.IDX_first_lightning_strength], # 首次闪电发生强度 （kA）；
                    'first_lw_level': arrs[TlpApp.IDX_first_lw_level], # 首次闪电发生时对应的设备预警等级；
                    'last_lightning_time': arrs[TlpApp.IDX_last_lightning_time], # 预警持续时间内末次闪电发生时间；
                    'last_lightning_lng': arrs[TlpApp.IDX_last_lightning_lng], # 末次闪电发生经度；
                    'last_lightning_lat': arrs[TlpApp.IDX_last_lightning_lat], # 末次闪电发生纬度；
                    'last_lightning_distance': arrs[TlpApp.IDX_last_lightning_distance], # 末次闪电距设备的距离（km）；
                    'last_lightning_strength': arrs[TlpApp.IDX_last_lightning_strength], # 末次闪电发生强度 （kA）；
                    'last_lw_level': arrs[TlpApp.IDX_last_lw_level], # 末次闪电发生时对应的设备预警等级。
                }
                if item['last_lw_level'] == '':
                    FP_num += 1 # 误报
                else:
                    if item['max_elevel'] == '5':
                        TP_num += 1 # 正确报警
                    else:
                        FP_num += 1
        print(f'统计结果：TP={TP_num}; FP={FP_num}; 精度：{TP_num/(TP_num+FP_num)}')

def main(params:Dict = {}) -> None:
    TlpApp.startup(params=params)

def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--run_mode', action='store',
        type=int, default=1, dest='run_mode',
        help='run mode'
    )
    return parser.parse_args()

if '__main__' == __name__:
    args = parse_args()
    params = vars(args)
    main(params=params)
